package com.example.service.impl;

import com.example.domain.LoginUser;
import com.example.domain.ResponseResult;
import com.example.domain.entity.User;
import com.example.domain.enums.AppHttpCodeEnum;
import com.example.domain.po.MenuPO;
import com.example.domain.po.RolePO;
import com.example.domain.po.UserPO;
import com.example.domain.vo.SystemVo;
import com.example.domain.vo.UserVo;
import com.example.exception.SystemException;
import com.example.mapper.MenuMapper;
import com.example.mapper.RoleMapper;
import com.example.mapper.UserMapper;
import com.example.service.SystemService;
import com.example.util.BeanCopyUtil;
import com.example.util.JwtUtil;
import com.example.util.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.example.config.RabbitMqConfig.*;

/**
 * @author hlc
 * @version 1.0
 * @date 2022/10/14 21:38
 */
@Slf4j
@Service
public class SystemServiceImpl implements SystemService {
    private final AuthenticationManager authenticationManager;
    private final MenuMapper menuMapper;
    private final RoleMapper roleMapper;
    private final UserMapper userMapper;
    private final RedisCache redisCache;
    private final RabbitTemplate rabbitTemplate;
    private final PasswordEncoder passwordEncoder;

    public SystemServiceImpl(AuthenticationManager authenticationManager, MenuMapper menuMapper, RoleMapper roleMapper, UserMapper userMapper, RedisCache redisCache, RabbitTemplate rabbitTemplate, PasswordEncoder passwordEncoder) {
        this.authenticationManager = authenticationManager;
        this.menuMapper = menuMapper;
        this.roleMapper = roleMapper;
        this.userMapper = userMapper;
        this.redisCache = redisCache;
        this.rabbitTemplate = rabbitTemplate;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public ResponseResult<SystemVo> login(User user) {
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authenticate)) {
            throw new SystemException(AppHttpCodeEnum.LOGIN_ERROR);
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String token = JwtUtil.createJWT(userId);
        // 把token存入redis
        redisCache.setCacheObject("login:" + userId, loginUser, 1, TimeUnit.HOURS);
        RolePO role = roleMapper.selectRoleByUserId(loginUser.getUser().getId());
        List<MenuPO> menus = menuMapper.selectMenuByRoleId(role.getId());
        UserVo userVo = BeanCopyUtil.copyBean(loginUser.getUser(), UserVo.class);
        SystemVo result = new SystemVo(token, userVo, menus);
        return ResponseResult.okResult(result);
    }

    @Override
    public ResponseResult<?> register(User user) {
        if (!user.getPassword().equals(user.getConfirm())) {
            throw new SystemException(AppHttpCodeEnum.ILLEGAL_DATA);
        }
        // 比对验证码
        Object code = redisCache.getCacheObject("register:" + user.getEmail());
        if (Objects.isNull(code)) {
            throw new SystemException(AppHttpCodeEnum.VERIFICATION_CODE_OVERDUE);
        }
        if (!code.toString().equals(user.getVerificationCode())) {
            throw new SystemException(AppHttpCodeEnum.VERIFICATION_CODE_ERROR);
        }
        // 密码加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        // 用户信息存入数据库
        Integer count = userMapper.insertUser(user);
        if (Objects.isNull(count)) {
            throw new SystemException(AppHttpCodeEnum.SYSTEM_ERROR);
        }
        log.info("新注册用户id为{}", user.getId());
        // 为用户设置初始角色
        roleMapper.insertInitialUserRole(user.getId());
        return ResponseResult.okResult("注册成功，请登录");
    }

    @Override
    public ResponseResult<?> sendRegisterCode(User user) {
        if (!StringUtils.hasText(user.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
        }
        // 判断key是否存在
        String key = "register:" + user.getEmail();
        if (redisCache.hasKey(key)) {
            throw new SystemException(AppHttpCodeEnum.OPERATE_FREQUENTLY);
        }
        rabbitTemplate.convertAndSend(DIRECT_EMAIL_EXCHANGE, REGISTER_EMAIL_ROUTING_KEY, user.getEmail());
        return new ResponseResult<>(AppHttpCodeEnum.VERIFICATION_CODE_SEND_SUCCESS);
    }

    @Override
    public ResponseResult<?> forget(User user) {
        if (!user.getPassword().equals(user.getConfirm())) {
            throw new SystemException(AppHttpCodeEnum.ILLEGAL_DATA);
        }
        // 比对验证码
        Object code = redisCache.getCacheObject("check:" + user.getEmail());
        if (Objects.isNull(code)) {
            throw new SystemException(AppHttpCodeEnum.VERIFICATION_CODE_OVERDUE);
        }
        if (!code.toString().equals(user.getVerificationCode())) {
            throw new SystemException(AppHttpCodeEnum.VERIFICATION_CODE_ERROR);
        }
        // 加密密码
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        // 修改数据库
        userMapper.updateByUserName(user);
        return ResponseResult.okResult("密码修改成功，请重新登陆");
    }

    @Override
    public ResponseResult<?> sendCheckCode(User user) {
        // 校验用户信息是否存在
        UserPO userInfo = userMapper.selectByUserNameAndEmail(user.getUserName(), user.getEmail());
        if (Objects.isNull(userInfo)) {
            throw new SystemException(AppHttpCodeEnum.USER_NOT_EXIST);
        }
        // 判断key是否存在
        String key = "check:" + user.getEmail();
        if (redisCache.hasKey(key)) {
            throw new SystemException(AppHttpCodeEnum.OPERATE_FREQUENTLY);
        }
        // 发送消息
        rabbitTemplate.convertAndSend(DIRECT_EMAIL_EXCHANGE, CHECK_EMAIL_ROUTING_KEY, user.getEmail());
        return new ResponseResult<>(AppHttpCodeEnum.VERIFICATION_CODE_SEND_SUCCESS);
    }

}
